package org.awa.demo.opencv.service.impl;

import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.awa.core.redis.service.IRedisService;
import org.awa.demo.opencv.bean.GeometryIntersectsBean;
import org.awa.demo.opencv.service.IDemoService;
import org.awa.demo.opencv.task.DemoTask;
import org.awa.demo.opencv.task.kunming.GroupItemDto;
import org.awa.demo.opencv.task.kunming.GroupPointItemDto;
import org.awa.demo.opencv.task.kunming.KunMingTask;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.grid.Grids;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileWriter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author xxf
 */
@Slf4j
@Service
public class DemoServiceImpl implements IDemoService {

    @Autowired
    private KunMingTask kunMingTask;

    @Autowired
    private IRedisService redisService;

    @Override
    public void importGeoJson(MultipartFile file) throws Exception{
        File fileLocal = getFileFromMulti(file);
        FeatureJSON fjson = new FeatureJSON();
        FeatureCollection featureCollection = fjson.readFeatureCollection(fileLocal);
        SimpleFeatureIterator features = (SimpleFeatureIterator) featureCollection.features();
        List<Geometry> geometryList = new ArrayList<>();
        while (features.hasNext()){
            SimpleFeature feature = features.next();
            Geometry geometry = (Geometry) feature.getAttribute("geometry");
            geometryList.add(geometry);
        }

        List<List<Geometry>> geometryListList = analysisGeometry(geometryList);

        List<Geometry> geometryUnionList = geometryListList.stream().map(beanList -> {
            Geometry[] geometries = new Geometry[beanList.size()];
            for (int i = 0; i < beanList.size(); i++) {
                geometries[i] = beanList.get(i);
            }
            GeometryFactory geometryFactory = new GeometryFactory();
            GeometryCollection geometryCollection = geometryFactory.createGeometryCollection(geometries);
            Geometry unionGeomery = geometryCollection.union();

            log.info("result - " + unionGeomery.toText());
            return unionGeomery;
        }).collect(Collectors.toList());

        createGeojson(geometryUnionList,fileLocal.getName());
    }

    private void createGeojson(List<Geometry> geometryList,String fileName) throws Exception{
        SimpleFeatureTypeBuilder featureTypeBuildBuilder = new SimpleFeatureTypeBuilder();
        featureTypeBuildBuilder.setName("zijingang-union");
        featureTypeBuildBuilder.add("geometry", Polygon.class);
        featureTypeBuildBuilder.add("id",String.class);
        featureTypeBuildBuilder.add("height",String.class);
        SimpleFeatureType featureBuildType = featureTypeBuildBuilder.buildFeatureType();

        List<SimpleFeature> features = new ArrayList<>();

        //创建feature生成器
        for (int i = 0; i < geometryList.size(); i++) {
            Geometry geometry = geometryList.get(i);
            SimpleFeatureBuilder featureBuildBuilder = new SimpleFeatureBuilder(featureBuildType);
            featureBuildBuilder.add(geometry);
            featureBuildBuilder.add(i);
            featureBuildBuilder.add(50);
            SimpleFeature feature = featureBuildBuilder.buildFeature(String.valueOf(i));
            features.add(feature);
        }

        SimpleFeatureCollection collection = new ListFeatureCollection(featureBuildType,features);

        String filePath = String.format("D:\\gisLearn\\zijingang\\result\\%s",fileName);
        FileWriter fileWriter = new FileWriter(new File(filePath));
        FeatureJSON fjson = new FeatureJSON(new GeometryJSON(13));
        fjson.writeFeatureCollection(collection,fileWriter);
        fileWriter.close();
    }

    private File getFileFromMulti(MultipartFile file) throws Exception{
        File fileLocal = new File("D://demotest/"+ DateUtil.format(new Date(),"yyyyMMdd") + "/" + file.getOriginalFilename());
        FileUtil.writeFromStream(file.getInputStream(),fileLocal);
        return fileLocal;
    }

    /**
     *
     *
     * @param geometryList
     * @return
     */
    private List<List<Geometry>> analysisGeometry(List<Geometry> geometryList){
        if(geometryList.size() == 0){
            return new ArrayList<>();
        }
        List<GeometryIntersectsBean> oldList = geometryList.parallelStream().map(geometry -> {
            return new GeometryIntersectsBean(geometry);
        }).collect(Collectors.toList());

        for (GeometryIntersectsBean geometryIntersectsBeanLeft : oldList){
            for (GeometryIntersectsBean geometryIntersectsBeanRight : oldList){
                if(geometryIntersectsBeanLeft == geometryIntersectsBeanRight){
                    continue;
                }
                if(geometryIntersectsBeanLeft.getGeometry().intersects(geometryIntersectsBeanRight.getGeometry())){
                    geometryIntersectsBeanLeft.addLink(geometryIntersectsBeanRight);
                }
            }
        }

        List<List<Geometry>> resultList = new ArrayList<>();

        List<GeometryIntersectsBean> unionList = new ArrayList<>();
        int index = 0;

        while (oldList.size() > 0){
            if(unionList.size() == 0){
                unionList.add(oldList.remove(0));
            }
            GeometryIntersectsBean geometryIntersectsBean = unionList.get(index);
            List<GeometryIntersectsBean> linkList = geometryIntersectsBean.getLink();
            boolean hasAdd = false;
            for (int i = 0; i < linkList.size(); i++) {
                GeometryIntersectsBean linkGeometry = linkList.get(i);
                if(!unionList.contains(linkGeometry)){
                    unionList.add(linkGeometry);
                    oldList.remove(linkGeometry);
                    hasAdd = true;
                }
            }
            if(!hasAdd || oldList.size() == 0){
                resultList.add(unionList.stream().map(bean -> {
                    bean.clearLink();
                    return bean.getGeometry();
                }).collect(Collectors.toList()));
                unionList.clear();

                index = 0;
            }else{
                index++;
            }
        }

        return resultList;
    }

    /**
     * 转换为墨卡托坐标系
     * 所gis元素操作，都需要使用墨卡托坐标系，普通坐标系以度为单位，没用。墨卡托以米为单位
     *
     * @param geometry
     * @param crs
     * @return
     * @throws Exception
     */
    public static Geometry getMercatorGeometry(Geometry geometry,String crs,String targetCrs) throws Exception{
        CoordinateReferenceSystem sourceCRS = CRS.decode(crs);
        CoordinateReferenceSystem targetCRS = CRS.decode(targetCrs);
        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
        return JTS.transform(geometry, transform);
    }

    /**
     * 六边形栅格化
     *
     * @param boundary
     * @param sideLen
     * @return
     * @throws Exception
     */
    public static List<Geometry> rasterizeHexagonalGrid(Geometry boundary,double sideLen) throws Exception{
        List<Geometry> gridList = new ArrayList<>();

        Envelope envelopeInternal = boundary.getEnvelopeInternal();

        //栅格化<六边形>
        SimpleFeatureSource grid = Grids.createHexagonalGrid(new ReferencedEnvelope(
                envelopeInternal.getMinX(),envelopeInternal.getMaxX(),
                envelopeInternal.getMinY(),envelopeInternal.getMaxY(),
                DefaultGeographicCRS.WGS84),sideLen);//WGS84 说是没用，看看效果再说
        SimpleFeatureIterator iterator = grid.getFeatures().features();

        while (iterator.hasNext()){
            SimpleFeature feature = iterator.next();
            Geometry geometry = (Geometry) feature.getDefaultGeometry();
            if(boundary.crosses(geometry)){
                gridList.add(geometry);
            }
        }
        return gridList;
    }

    /**
     * 根据wkt获取geometry
     *
     * @param wkt
     * @return
     * @throws Exception
     */
    public static Geometry getGeometryByWKT(String wkt) throws Exception{
        if(StringUtils.isEmpty(wkt)){
            return null;
        }
        GeometryFactory geometryFactory = new GeometryFactory();
        WKTReader reader = new WKTReader(geometryFactory);
        Geometry geometry = reader.read(wkt);
        return geometry;
    }

    @Override
    public void calByNoise(){
        Thread thread = new Thread(new DemoTask());
        thread.start();
    }

    @Override
    public List<GroupItemDto> calByKunMing() throws Exception{
        List<GroupItemDto> run = kunMingTask.run();
        return run;
    }

    @Override
    public GroupPointItemDto calByKunMingNew(GroupItemDto request) throws Exception{
        GroupPointItemDto result = new GroupPointItemDto();
        BeanUtil.copyProperties(request, result);

        String taskId = UUID.randomUUID().toString().replaceAll("-","");
        result.setTaskId(taskId);

        List<List<Integer>> dataList = request.getData().get(0);
        int pageSize = (int) Math.ceil((double) dataList.size() / 1000);
        result.setPSize(pageSize);

        for (int i = 1; i <= pageSize; i++) {
            int startIndex = (i - 1) * 1000;
            int endIndex = startIndex + 1000;
            if(endIndex > dataList.size()){
                endIndex = dataList.size();
            }
            List<List<Integer>> dataSubList = dataList.subList(startIndex, endIndex);

            String collect = dataSubList.parallelStream().map(point -> {
                return String.format("%s|%s|%s", point.get(0), point.get(1), point.get(2));
            }).collect(Collectors.joining(","));
            redisService.vSet(getKeyInRedis(taskId,i),collect,10, TimeUnit.MINUTES);
        }

        return result;
    }

    @Override
    public List<List<Integer>> getKunMingPage(String taskId, Integer pNum) throws Exception{

        String content = redisService.vGet(getKeyInRedis(taskId, pNum));
        if(StringUtils.isEmpty(content)){
            return new ArrayList<>();
        }

        List<List<Integer>> dataList = Stream.of(content.split(",")).parallel().map(item -> {
            List<Integer> dataItem = new ArrayList<>();
            String[] split = item.split("\\|");
            dataItem.add(Integer.valueOf(split[0]));
            dataItem.add(Integer.valueOf(split[1]));
            dataItem.add(Integer.valueOf(split[2]));
            return dataItem;
        }).collect(Collectors.toList());
        log.info("kunming page : {}",dataList.size());
        return dataList;
    }

    private String getKeyInRedis(String taskId,Integer pNum){
        return String.format("page_sub_list:%s:%s",taskId,pNum);
    }
}
